/*
 * This file is part of the Sx Framework Library.
 * 
 * Copyright (C) 2013 University of Colorado Denver
 * <min.choi@ucdenver.edu> <shane.transue@ucdenver.edu>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 */
#ifndef SX_TRANSFORMATION_H
#define SX_TRANSFORMATION_H

/*
 * The primitive internal structures of the Transformation class rely on
 * the basic linear algebra types in the Eigen library.
 */
#include <sxMath.h>

namespace Sx {
namespace Math {

class Transformation {
public:
	Transformation();
	Transformation(const Transformation& transform);
	virtual ~Transformation();

	/*
	 * Compiles the individual components in the transformation into a single
	 * matrix that can be retrieved quickly. This internal matrix can be 
	 * retrieved with the toMatrix4f() function. (Intended for rendering)
	 *
	 * TODO: Boolean for row or col major.
	 */
	void compile();

	/* Returns the internal position of this transformation. */
	Eigen::Vector3f getPosition() const;

	/* Returns the scale of this transformation. */
	Eigen::Vector3f getScale() const;

	/* Returns the rotation of this transformation. */
	Eigen::Quaternionf getRotation() const;

	/*
	 * This function returns the internally compiled transformation matrix.
	 * This function returns a reference to the internal matrix because for 
	 * efficiency, the matrix only needs to be compiled when changes are made.
	 * If changes have been made compile must be called before they can be seen
	 * in the transformation matrix.
	 */
	const Eigen::Matrix4f toMatrix4f() const;

	/*
	 * Adds the provided value to the existing position value. These operations
	 * do not require a recompilation of the internal matrix.
	 */
	void addPositionX(float x);
	void addPositionY(float y);
	void addPositionZ(float z);
	void addPosition(float x, float y, float z);
	void addPosition(const Eigen::Vector3f& displacement);

	/*
	 * Sets the positional values of this transformation. These operations do
	 * not require a recompilation of the internal matrix.
	 */
	void setPositionX(float x);
	void setPositionY(float y);
	void setPositionZ(float z);
	void setPosition(float x, float y, float z);
	void setPosition(const Eigen::Vector3f& position);

	/*
	 * Adds the provided value to the existing scale value. This forces a
	 * recompilation of the internal matrix.
	 */
	void addScaleX(float x);
	void addScaleY(float y);
	void addScaleZ(float z);
	void addScale(float x, float y, float z);
	void addScale(const Eigen::Vector3f& scale);

	/*
	 * Set the scale values of this transformation. This forces a recompilation
	 * of the internal matrix.
	 */
	void setScaleX(float x);
	void setScaleY(float y);
	void setScaleZ(float z);
	void setScale(float x, float y, float z);
	void setScale(const Eigen::Vector3f& scale);

	/*
	 * Sets the rotation of this transformation to the provided rotation.
	 * This operation requires a recompilation of the internal matrix.
	 */
	void setRotation(const Eigen::Quaternionf& rotation);

	/*
	 * Sets the rotational component of this transformation to a simple
	 * rotation about the X axis.
	 */
	void addRotationX(float angle);

	/*
	 * Sets the rotational component of this transformation to a simple
	 * rotation about the Y axis.
	 */
	void addRotationY(float angle);

	/*
	 * Sets the rotational component of this transformation to a simple
	 * rotation about the Z axis.
	 */
	void addRotationZ(float angle);

	/*
	 * Adds the provided rotation to the existing rotation.
	 * This operation requires a recompilation of the internal matrix.
	 */
	void addRotation(const Eigen::Quaternionf& rotation);
	
	/*
	 * Sets the internal transformation matrix to the provided. Note that if
	 * this function is used rather than the set*(..) functions, this matrix
	 * will be overwritten by the compile function (which is called whenever
	 * any of the set*(..) functions are called).
	 */
	void fromMatrixOnly(const Eigen::Matrix4f& transform);

	/*
	 * Constructions a quaternion based rotation based on a set of Euler angles
	 */
	static Eigen::Quaternionf EulerRotation(float psi, float phi, float theta);

	/*
	 * Returns a transformation with the identity values loaded for every
	 * component contained internally.
	 */
	static Transformation Identity();

	/*
	 * Basic assignment operator for a Transformation.
	 */
	Transformation& operator = (const Transformation& t);

protected:
	/*
	 * Holds the currently compiled transformation matrix. The position, scale,
	 * and rotation are compiled to a 4x4 matrix representation for quick
	 * access. This transformation should only be updated as required. Some
	 * operations (involving scale, rotation functions) will force the matrix
	 * to be recompiled. This matrix is internally maintained for performance.
	 */
	Math::Matrix4f transform;

	/*
	 * Maintains the position in this transformation.
	 */
	Math::Vector3f position;

	/*
	 * Maintains the scale in this transformation.
	 */
	Math::Matrix3f scale;

	/*
	 * Represents the rotation in this transformation.
	 */
	Math::Quaternionf rotation;
};

}

}

#endif
